package com.example.plugin.config;


import com.example.plugin.setting.GenerateSetting;
import com.example.plugin.util.DataUtil;
import com.intellij.database.model.*;
import com.intellij.database.psi.DbTable;
import com.intellij.database.util.DasUtil;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.ui.Messages;
import com.intellij.psi.PsiElement;
import com.intellij.util.containers.JBIterable;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.apache.commons.lang.StringUtils;

import java.io.*;
import java.util.*;

public class GenerateConfig {

    private PanelModel panelModel;

    private AnActionEvent anActionEvent;

    private Map<String,String> attributesMap = new HashMap<>();

    public GenerateConfig() {
    }


    public void execute() throws IOException {
        PsiElement[] psiElements = this.anActionEvent.getData(LangDataKeys.PSI_ELEMENT_ARRAY);
        if (psiElements == null || psiElements.length != 1) {
            Messages.showMessageDialog("Please select only one or more tables", "Notice", Messages.getInformationIcon());
            return;
        }
        PsiElement psiElement = psiElements[0];
        RawConnectionConfig connectionConfig = ((DbTable)psiElement).getDataSource().getConnectionConfig();
        String driverClass = connectionConfig.getDriverClass();
        String dataBaseType = null;
        if(driverClass.contains("mysql")){
            dataBaseType = "mysql";
        }else if(driverClass.contains("postgresql")){
            dataBaseType = "postgresql";
        }else{
            Messages.showMessageDialog("不支持的数据库类型", "Notice", Messages.getInformationIcon());
            return;
        }
        JBIterable<? extends DasColumn> columns = DasUtil.getColumns((DbTable) psiElement);
        List<GenerateModel> generateModels = new ArrayList<>();
        for(DasColumn column:columns){
            GenerateModel generateModel = DataUtil.build(dataBaseType, column);
            generateModels.add(generateModel);
        }
        //需要生成文件的类型集合
        List<TypeEnum> typeEnums = addTypeList();
        //生成文件信息
        putAttributesMap(typeEnums);
        //生成文件
        createFile(generateModels, typeEnums);
    }

    /**
     * 组成可生成的类属性，并放入attributesMap
     */
    public void putAttributesMap(List<TypeEnum> typeEnums){
        //XXXEntity
        String entityName = panelModel.getEntityName();
        this.attributesMap.put(Constant.ENTITY_CLASS_NAME,entityName);

        String className = entityName.replace("Entity","");
        //DAO
        String daoInterfaceName = className+"Dao";
        this.attributesMap.put(Constant.DAO_INTERFACE_NAME,daoInterfaceName);
        //Service
        String serviceInterfaceName = className+"Service";
        this.attributesMap.put(Constant.SERVICE_INTERFACE_NAME,serviceInterfaceName);
        //ServiceImpl
        String serviceClassName = className+"ServiceImpl";
        this.attributesMap.put(Constant.SERVICE_CLASS_NAME,serviceClassName);
        //Controller
        String controllerClassName = className+"Controller";
        this.attributesMap.put(Constant.CONTROLLER_CLASS_NAME,controllerClassName);

        PanelModel panelModel = this.getPanelModel();
        typeEnums.forEach(item->{
            if(item.equals(TypeEnum.ENTITY)){
                String entityClassPath = panelModel.getEntityPath();
                this.attributesMap.put(Constant.ENTITY_CLASS_PATH,entityClassPath);
                String entityClassPackage = getPackageName(item,entityClassPath);
                this.attributesMap.put(Constant.ENTITY_CLASS_PACKAGE,entityClassPackage);
                String entityClassFileName = entityClassPath+File.separator+entityName+".java";
                this.attributesMap.put(Constant.ENTITY_CLASS_FILE_NAME,entityClassFileName);
            }else if(item.equals(TypeEnum.DAO)){
                String daoInterfacePath = panelModel.getDaoPath();
                this.attributesMap.put(Constant.DAO_INTERFACE_PATH,daoInterfacePath);
                String daoInterfacePackage = getPackageName(item,daoInterfacePath);
                this.attributesMap.put(Constant.DAO_INTERFACE_PACKAGE,daoInterfacePackage);
                String daoInterfaceFileName = daoInterfacePath+File.separator+daoInterfaceName+".java";
                this.attributesMap.put(Constant.DAO_INTERFACE_FILE_NAME,daoInterfaceFileName);
                this.attributesMap.put(Constant.NAMESPACE,daoInterfacePackage+daoInterfaceName);
            }else if(item.equals(TypeEnum.SERVICE)){
                String serviceInterfacePath = panelModel.getServicePath();
                this.attributesMap.put(Constant.SERVICE_INTERFACE_PATH,serviceInterfacePath);
                String serviceInterfacePackage = getPackageName(item,serviceInterfacePath);
                this.attributesMap.put(Constant.SERVICE_INTERFACE_PACKAGE,serviceInterfacePackage);
                serviceInterfacePath = serviceInterfacePath+File.separator+"intf";
                String serviceInterfaceFileName = serviceInterfacePath+File.separator+serviceInterfaceName+".java";
                this.attributesMap.put(Constant.SERVICE_INTERFACE_FILE_NAME,serviceInterfaceFileName);
            }else if(item.equals(TypeEnum.SERVICEIMPL)){
                String serviceClassPath = panelModel.getServicePath();
                this.attributesMap.put(Constant.SERVICE_CLASS_PATH,serviceClassPath);
                String serviceClassPackage = getPackageName(item,serviceClassPath);
                this.attributesMap.put(Constant.SERVICE_CLASS_PACKAGE,serviceClassPackage);
                serviceClassPath = serviceClassPath+File.separator+"impl";
                String serviceClassFileName = serviceClassPath+File.separator+serviceClassName+".java";
                this.attributesMap.put(Constant.SERVICE_CLASS_FILE_NAME,serviceClassFileName);
            }else if(item.equals(TypeEnum.CONTROLLER)){
                String controllerClassPath = panelModel.getControllerPath();
                this.attributesMap.put(Constant.CONTROLLER_CLASS_PATH,controllerClassPath);
                String controllerClassPackage = getPackageName(item,controllerClassPath);
                this.attributesMap.put(Constant.CONTROLLER_CLASS_PACKAGE,controllerClassPackage);
                String controllerClassFileName = controllerClassPath+File.separator+controllerClassName+".java";
                this.attributesMap.put(Constant.CONTROLLER_CLASS_FILE_NAME,controllerClassFileName);
            }else if(item.equals(TypeEnum.SQLXML)){
                String xmlPath = panelModel.getXmlPath();
                String xmlFileName = xmlPath+File.separator+daoInterfaceName+".xml";
                this.attributesMap.put(Constant.XML_FILE_NAME,xmlFileName);
            }
        });
    }

    /**
     * 需要生成的文件集合
     * @return
     */
    public List<TypeEnum> addTypeList(){
        PanelModel panelModel = this.getPanelModel();
        List<TypeEnum> typeEnums = new ArrayList<>();
        if(panelModel.isEntityCheck()){
            typeEnums.add(TypeEnum.ENTITY);
        }
        if(panelModel.isDaoCheck()){
            typeEnums.add(TypeEnum.DAO);
        }
        if(panelModel.isServiceCheck()){
            typeEnums.add(TypeEnum.SERVICE);
            typeEnums.add(TypeEnum.SERVICEIMPL);
        }
        if(panelModel.isControllerCheck()){
            typeEnums.add(TypeEnum.CONTROLLER);
        }
        if(panelModel.isXmlCheck()){
            typeEnums.add(TypeEnum.SQLXML);
        }
        return typeEnums;
    }

    public void createFile(List<GenerateModel> generateModels,List<TypeEnum> typeEnums) throws IOException {
        Configuration configuration = new Configuration(Configuration.VERSION_2_3_0);
        PanelModel panelModel = this.getPanelModel();
        for(TypeEnum typeEnum : typeEnums){
            String templateName = typeEnum.getCode();
            Template template = new Template(templateName,new StringReader(GenerateSetting.settings.get(templateName).getText()),configuration);
            Map<String, Object> dataMap = new HashMap<String, Object>();
            dataMap.put("tableName",panelModel.getTableName());
            dataMap.put("attrs",generateModels);
            dataMap.putAll(this.attributesMap);
            Set<String> set = new HashSet<>();
            for(GenerateModel generateModel:generateModels){
                set.add(generateModel.getType());
            }
            dataMap.put("types",set);
            if(TypeEnum.ENTITY.equals(typeEnum)){
                createFile(template,attributesMap.get(Constant.ENTITY_CLASS_FILE_NAME),dataMap);
            }else if(TypeEnum.DAO.equals(typeEnum)){
                createFile(template,attributesMap.get(Constant.DAO_INTERFACE_FILE_NAME),dataMap);
            }else if(TypeEnum.SERVICE.equals(typeEnum)){
                createFile(template,attributesMap.get(Constant.SERVICE_INTERFACE_FILE_NAME),dataMap);
            }else if(TypeEnum.SERVICEIMPL.equals(typeEnum)){
                createFile(template,attributesMap.get(Constant.SERVICE_CLASS_FILE_NAME),dataMap);
            }else if(TypeEnum.CONTROLLER.equals(typeEnum)){
                createFile(template,attributesMap.get(Constant.CONTROLLER_CLASS_FILE_NAME),dataMap);
            }else if(TypeEnum.SQLXML.equals(typeEnum)){
                createFile(template,attributesMap.get(Constant.XML_FILE_NAME),dataMap);
            }
        }

    }


    public String getPackageName(TypeEnum typeEnum, String path){
        String basePackageName = null;
        String packageName = null;
        if(path.contains("java")){
            if(path.length()>(path.indexOf("java")+4)){
                basePackageName = path.substring(path.indexOf("java")+5).replace("\\",".").replace("/",".");
            }else{
                basePackageName = path;
            }
        }else{
            basePackageName = path;
        }
        if(TypeEnum.ENTITY.equals(typeEnum)){
            packageName = "";
        }else if(TypeEnum.DAO.equals(typeEnum)){
            packageName = "";
        }else if(TypeEnum.SERVICE.equals(typeEnum)){
            packageName = "intf";
        }else if(TypeEnum.SERVICEIMPL.equals(typeEnum)){
            packageName = "impl";
        }else if(TypeEnum.CONTROLLER.equals(typeEnum)){
            packageName = "";
        }
        if(!StringUtils.isEmpty(packageName)){
            basePackageName = basePackageName+"."+packageName;
        }
        return basePackageName;
    }

    private void createFile(Template template,String fileName,Map<String,Object> dataMap) throws IOException {
        int i = fileName.lastIndexOf("\\");
        File floder = new File(fileName.substring(0,i));
        if(!floder.exists()){
            floder.mkdirs();
        }
        File file = new File(fileName);
        Writer out = null;
        try {
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
            template.process(dataMap, out);
        } catch (TemplateException | FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(out!=null){
                out.close();
            }
        }

    }

    public AnActionEvent getAnActionEvent() {
        return anActionEvent;
    }

    public void setAnActionEvent(AnActionEvent anActionEvent) {
        this.anActionEvent = anActionEvent;
    }

    public PanelModel getPanelModel() {
        return panelModel;
    }

    public void setPanelModel(PanelModel panelModel) {
        this.panelModel = panelModel;
    }


}
